#define __pool_h_
#include "pool.h"

int create_pool(tPool **pool, int max_thread_num){
    *pool = calloc(1, sizeof(tPool));
    if(!pool){
        printf("alloc pool failed !\n");
        exit(-1);
    }

    (*pool)->max_thread_num = max_thread_num;
    (*pool)->off = 0;
    (*pool)->task_queue_head = NULL;

    if(pthread_mutex_init(&((*pool)->queue_lock), NULL)){
        printf("pthread_mutex_init failed !\n");
        exit(-1);
    }

    if(pthread_cond_init(&((*pool)->queue_ready), NULL)){
        printf("pthread_cond_init failed !\n");
        exit(-1);
    }

    (*pool)->tid = calloc(max_thread_num, sizeof(pthread_t));
    if(!(*pool)->tid){
        printf("calloc tids failed !\n");
        exit(-1);
    }

    for(int i = 0; i < max_thread_num; i++){
        if(pthread_create(&((*pool)->tid[i]), NULL, run_task, (void *)*pool)){
            printf("pthread_create thread %d failed !\n", i);
            exit(-1);
        }
    }

    return 0;
}

int destroy_pool(tPool *pool){
    if(pool->off){
        printf("the pool had been destroyed !\n");
        return 0;
    }
    int i = 0;
    tNode *task_node;

    pthread_mutex_lock(&pool->queue_lock);
    pool->off = 1;
    pthread_cond_broadcast(&pool->queue_ready);
    pthread_mutex_unlock(&pool->queue_lock);

    for(i = 0; i < pool->max_thread_num; i++){
        pthread_join(pool->tid[i], NULL);
    }
    free(pool->tid);

    while(pool->task_queue_head){
        task_node = pool->task_queue_head;
        pool->task_queue_head = task_node->next;
        free(task_node);
    }

    pthread_mutex_destroy(&pool->queue_lock);
    pthread_cond_destroy(&pool->queue_ready);
    free(pool);
    return 0;
}

void * run_task(void * arg) {
    tPool * pool = (tPool *) arg;
    tNode * task;

    while(1){
        pthread_mutex_lock(&pool->queue_lock);
        // printf("cond once\n");
        while(!pool->off && !pool->task_queue_head){
            pthread_cond_wait(&pool->queue_ready, &pool->queue_lock);
        }
        // printf("get cond\n");
        if(pool->off){
            pthread_mutex_unlock(&pool->queue_lock);
            // printf("pool has been shutdown. \tthread exit %u. \n", pthread_self());
            pthread_exit(NULL);
        }

        task = pool->task_queue_head;
        pool->task_queue_head = task->next;
        pthread_mutex_unlock(&pool->queue_lock);

        task->task_func(task->arg);
        free(task);
    }
    return 0;
}

int add_task(tPool *pool, void * (*task_func)(void*), void *arg) {
    if(!task_func){
        printf("invalid task_function given\n");
        return -1;
    }

    tNode * task, *p;

    task = malloc(sizeof(tNode));
    if(!task){
        printf("task allocation failed!\n");
        return -1;
    }
    task->task_func = task_func;
    task->arg = arg;
    task->next = NULL;

    pthread_mutex_lock(&pool->queue_lock);
    // printf("get queue lock\n");
    if(!pool->task_queue_head){
        pool->task_queue_head = task;
    } else {
        p = pool->task_queue_head;
        while(p->next){
            p = p->next;
            // printf("in while loop\n");
        }
        p->next = task;
    }
    // printf("in add task loop\n");

    pthread_cond_signal(&pool->queue_ready);
    pthread_mutex_unlock(&pool->queue_lock);
    // printf("add succeeded\n");
    return 0;
}

// lamport

int entering[20] = {0};
int numbers[29] = {0};

int maxNumber(){
    int max = numbers[0];
    for(int i = 1; i < 20; ++i){
        if(max < numbers[i]){
            max = numbers[i];
        }
    }

    return max;
}

int priority(int i, int j){
    // 如果进程i可以运行则返回真
    if(!numbers[i]){
        return 0;   // 未在等待
    }else if(numbers[i] < numbers[j]){
        return 1;
    }else if(numbers[i] > numbers[j]){
        return 0;
    }else{
        // 这里必须用 <= 不然会被自己阻塞产生死锁
        return i <= j;
    }
}

void lamport_lock(int id){
    entering[id] = 1;
    numbers[id] = 1 + maxNumber();
    entering[id] = 0;

    for(int i = 0; i < 20; i++){
        while(entering[id]);
        while(numbers[i] && !priority(id, i));
    }
    return ;
}

void lamport_unlock(int id){
    numbers[id] = 0;
}